package com.ltu.myannotation;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import javax.annotation.Resource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

/**
 * @Description: 使用注解通知aop哪些方法需要进行缓存拦截
 * @author: 若尘
 * @date 2019年4月13日 下午1:51:31
 * @version V1.0
 */
@Aspect
@Slf4j
@Component  //注解通知
public class MyRedisAnnotationAop {
	
    @Resource
    RedisTemplate<String,Object> redisTemplate;
    
//	// 定义切入点
//	@Pointcut("excution(public com.ltu.service.impl.* .*(..))")
//	public void redisPoint() {
//	};

	// 之前处理 可用作统一的参数检查
	// @Before("redisPoint()")
	public void beforeActive(JoinPoint jp) {
		log.info("\n调用的实例对象：\t" + jp.getTarget() + "\t 进入方法：" + jp.getSignature().getName() + "\t 入参："
				+ Arrays.toString(jp.getArgs()));
	}

	/**
     * 配置环绕通知 + annotation的切点表达式
     * @annotation 注解形式的通知, 那个方法上添加了该注解, 则执行该通知
     */
	@Around("@annotation(com.ltu.myannotation.MyReidsCache)")
	public Object AroundActive(ProceedingJoinPoint jp) throws Throwable {
		
		try {
			//获取方法对象
			MethodSignature methodSignature=(MethodSignature) jp.getSignature();
			Method  method=methodSignature.getMethod();
			//获取方法上的缓存注解
			MyReidsCache myReidsCache=method.getAnnotation(MyReidsCache.class);
			//缓存的开启控制到单个对象上面，默认不开启缓存
			if(myReidsCache.enable()==false){
				 log.info("\n 不缓存策略");
				 return jp.proceed();
			}
				//获取传入的参数对象 
				Object[]  params=jp.getArgs();
				//获取写入redis的key=类名+方法名+参数列表
				StringBuilder  subKey=new StringBuilder();
//				subKey.append(jp.getTarget().getClass().getName());//获取类名
//				subKey.append(jp.getSignature().getName());//方法名
				subKey.append(myReidsCache.key().toString());
//				subKey.append(":");
				//获取参数值 支持Spel表达式
//				if(params!=null){
//					int len=params.length;
//					for(int i=0;i<len;i++){
//						System.out.println("\t 缓存aop获取到的参数值为："+params[i]);
//						subKey.append(params[i]);//获取参数
//					}
//				}
				
				if(!myReidsCache.fieldKey().equals("")){
					 Object idValue= MyAopUtil.parseKey(myReidsCache.fieldKey(), method, params);
					 if(idValue==null){
						 log.info("\n 获取到的缓存key值为：null, 添加操作无法获取主键，不进行缓存");
						return jp.proceed();
					 }
					subKey.append(idValue);
				}
				//最终得到的key
				String	key=subKey.toString();
//				System.out.println("\t 缓存对象得到的Key为:"+key);
				ValueOperations<String, Object> opertions=redisTemplate.opsForValue();
				Object  obj=null;
				byte[] values=null;
				//如果在缓存中找到数据则直接序列化返回
				if(redisTemplate.hasKey(key) && myReidsCache.compelUpdate()==false){
					obj= opertions.get(key);
//					ByteArrayInputStream bais=new ByteArrayInputStream(values);
//					ObjectInputStream ois=new ObjectInputStream(bais);
//					obj= ois.readObject();
					return obj;
				}else if(redisTemplate.hasKey(key) && myReidsCache.compelUpdate()==true){
					redisTemplate.delete(key);
				}
				//执行方法写入缓存
				obj=jp.proceed();
//				ByteArrayOutputStream baos=new ByteArrayOutputStream();
//				ObjectOutputStream oos=new ObjectOutputStream(baos);
//				oos.writeObject(obj);
//				values= baos.toByteArray();
				if(obj != null)
					opertions.set(key, obj, myReidsCache.expireTime(), myReidsCache.timeUnit());
				
			return obj;
		} catch (Throwable e) {
			log.error("\n入口：" + jp.getTarget() + "\n方法：" + jp.getSignature().getName() + "\n异常：" + e.toString());
			throw e;
		}
		
	}

}
